/**
 * Library to generate an HTML IReport
 */
/**
 * Canvas class. To create a canvas that holds the pages of the iReport
 */
var constants = {
	SCROLL_WIDTH : 16
};

/**
 * Common function used in page and groupbox. this object takes the role of a page or groupbox here
 */
var ComponentFunctions = {
	/**
	 * To add a component to the page or groupbox
	 * @param {Object} component
	 */
	addComponent : function (component) {
		this.element.appendChild(component.element);
		var offsetHeight = Utility.getOffsetHeight(component.element);
		this.componentLimits.push(offsetHeight);
		this.components[offsetHeight] = component;
		if(component.constructor escaped.base.dir == Field) {
			component.setFieldValue(component.inputObj.field.value);
		}
	},
	/**
	 * To add an array of components to the container
	 * @param {Object} components
	 */
	addComponents: function(components){
		for (var i = 0; i < components.length; i++) {
			this.addComponent(components[i]);
		}
	},
	/**
	 * Returns an array of components after the specified position in the component
	 * @param {Object} position
	 */
	getComponentsFromPosition : function (position) {
		this.componentLimits.sort(function(a, b) {
			return a-b;
		});
		var i = 0;
		do {
			var nextHeight = this.componentLimits[i];
			i++;
		} while(nextHeight <= position);
		var remainingIndices = this.componentLimits.slice(i-1, this.componentLimits.length);
		var returnObj = [];
		for(i = 0; i < remainingIndices.length; i++) {
			var currentComponent = this.components[remainingIndices[i]];
			var margin = Utility.getComputedStyle(currentComponent.element, "top") - position;
			currentComponent.margin = margin;
			returnObj.push(currentComponent);
			position = Utility.getOffsetHeight(currentComponent.element);
		}
		return returnObj;
	},
	/**
	 * To set the id of a component
	 * @param {Object} id
	 */
	setId : function (id) {
		this.id = id;
	}
}

var Canvas = function (inputObj) {
	this._setCSSClasses();
	this.inputObj = inputObj;
	this.element = this._setComponent(inputObj.parentid);
	if(inputObj.properties) {
		this.properties = {
			decimalSeparator : inputObj.properties.decimalSeparator || ".",
			showPageNumber : inputObj.properties.showPageNumber,
			pageNumberStyles : inputObj.properties.pageNumberStyles
		}
	}
	this.pages = [];
}

Canvas.prototype = (function () {
	
	/**
	 * Checks if the iReport classes have been added. If not added, adds them to the document.
	 */
	var _setCSSClasses = function () {
		var head = document.getElementsByTagName("head")[0];
		var styleElmt = document.getElementById("ireportStyles") || Utility.createHTMLElement("style", {
			type : "text/css",
			id : "ireportStyles"
		}, head);
	    var styletext = '.heading {font-size: 44px;}\
						.fieldlabel {float : left;width : 156px;height : 25px;}\
						.field {float : left;width : 100px;height : 25px;}\
						.canvas {background-color: #EBEBEB;overflow: auto;}\
						.page {background-color: #FFFFFF;margin: 10px;position : relative;}\
						.page > div {position: absolute;font-family: Helvetica !important;}\
						.pagebreak {width:712px;height:1px;background-color: #000000;left: 43px;}\
						.fieldbox {clear: both;}\
						.fieldbox div {font-size:17px; font-family: Helvetica !important; text-align: left;}\
						.subheading {font-size: 26px;text-decoration: underline;}\
						.gridbox {width:712px;table-layout : fixed; word-break : break-all;}\
						.gridbox th {padding : 0px; font-family : Helvetica !important; vertical-align : middle;text-align : center;border-top : 1px solid #000000;border-bottom : 1px solid #000000;font-size: 15px;}\
						.gridbox td {text-align : left;font-family : Helvetica !important; font-size : 15px; padding : 0px;}';
	    if(styleElmt.styleSheet)    //for IE
	        styleElmt.styleSheet.cssText=styletext;
	    else                        //for browsers
	        styleElmt.innerHTML=styletext;
	}
	
	/**
	 * To set the HTML for the canvas.
	 * @param {Object} parentid - The parent element to which the canvas is to be attached
	 */
	function _setComponent(parentid) {
		var canvas = Utility.createHTMLElement("div", {
			"class" : "canvas"
		}, this.inputObj.parentid);
		this.element = canvas;
		var styles = this.inputObj.styles;
		if(styles) {
			styles.width = !styles.width ? styles.width : "836px"
			styles.height = !styles.height ? styles.height : "702px"
		} else {
			styles = {
				width : "836px",
				height : "702px"
			}
		}
		this.setStyles(styles);		
		return canvas;
	}

	/**
	 * To set styles for the canvas
	 * @param {Object} styleObj
	 */
	var setStyles = function (styleObj) {
		Utility.addStyles(this.element, styleObj);
	}
	
	/**
	 * To add a page to a canvas
	 * @param {Object} Page object
	 * @param {boolean} ignorePageNumber - if true, page numbers are not updated after adding the page
	 */
	var addPage = function (page, ignorePageNumber) {
		this.element.appendChild(page.element);
		this.pages.push(page);
		page.canvas = this;
		page.setPosition();
		Utility.addStyles(page.pageNumberElmt, page.canvas.properties.pageNumberStyles);
		!ignorePageNumber && setPageNumbers(this.pages, this.properties.showPageNumber);
	}
	
	/**
	 * To remove a page at the specified page number
	 * @param {Object} pageNumber
	 */
	var removePage = function (pageNumber) {
		var page = this.pages[pageNumber - 1];
		this.element.removeChild(page.element);
		page = null;
		this.pages.splice(pageNumber - 1, 1);
		setPageNumbers(this.pages);
	}
	
	/**
	 * Returns object with properties
	 * horizontal - true if the horizontal scroll is visible
	 * vertical - true if the vertical scroll is visible
	 */
	var isScrollVisible = function () {
		var returnObj = {};
		var page = this.pages[0];
		var pageHeight = Utility.removePx(page.element.style.height);
		var margin = Utility.removePx(page.element.style.margin);
		var canvasHeight = Utility.removePx(this.element.style.height);
		if((pageHeight + margin) > canvasHeight) {
			returnObj.vertical = true;
		}
		return returnObj;
	}
	
	/**
	 * To add multiple pages to a canvas
	 * @param {Object} Array of Page objects
	 */
	var addPages = function (pages) {
		for(var i = 0; i < pages.length; i++) {
			this.addPage(pages[i], false);
		}
		setPageNumbers(this.pages);
	}
	
	/**
	 * Set the page numbers
	 * @param {Object} pages
	 */
	var setPageNumbers = function (pages, showPageNumber) {
		var pageNumberStr = "";
		for(var i = 0; i < pages.length; i++) {
			if(showPageNumber) {
				pageNumberStr = (i+1) + " of " + pages.length;
			}
			pages[i].setPageNumber(pageNumberStr, i+1);			
		}
	}
	
	/**
	 * Returns all the components from all the pages including and after the mentioned page number
	 * @param {Object} pageNumber
	 */
	var getComponentsFromPage = function (pageNumber) {
		var componentsArray = [];
		for(var i = pageNumber - 1; i < this.pages.length; i++) {
			componentsArray.push(this.pages[i].getComponentsFromPosition(0));
		}
		return componentsArray;
	}
	
	return {
		_setCSSClasses : _setCSSClasses,
		_setComponent : _setComponent,
		addPage : addPage,
		addPages : addPages,
		setStyles : setStyles,
		isScrollVisible : isScrollVisible,
		constructor : Canvas,
		removePage : removePage,
		getComponentsFromPage : getComponentsFromPage
	}
})();

/**
 * Page class. To create a page. If the styles are not specified by default an A4 page is created
 * @param {Object} inputObj - object with the following attributes
 *  styles - object containing the styles
 */
var Page = function (inputObj) {
	this.inputObj = inputObj;
	this.element = this._setComponent();
	this.canvas = {};
	this.componentLimits = [];	//Array of offsetHeights of the components in the page
	this.components = {};	//Object with key as offsetHeight and component as the data
	this.pageNumber;	//will be filled from canvas on adding the page
	this.pageNumberElmt;	//holds the div element that displays the page number
};

Page.prototype = (function () {
	/**
	 * To set the HTML fot the page.
	 */
	var _setComponent = function () {
		var page = Utility.createHTMLElement("div", {
			"class" : "page"
		});
		this.element = page;
		var styles = this.inputObj && this.inputObj.styles;
		if(styles) {
			styles.width = !styles.width ? styles.width : "757px"
			styles.height = !styles.height ? styles.height : "977px"
		} else {
			styles = {
				width : "800px",
				height : "977px"
			}
		}	
		Utility.addStyles(page, styles);
		this.pageNumberElmt = Utility.createHTMLElement("div", {
			"style" : "top:" + (Utility.removePx(styles.height) - 29) + "px;left:" + (Utility.removePx(styles.width) - 88) + "px"
		}, page);		
		if(this.inputObj.pageMargin) {
			for(var i = 0; i < this.inputObj.pageMargin.length; i++) {
				this.setPageMargin(this.inputObj.pageMargin[i].side, this.inputObj.pageMargin[i].value);
			}
		}
		return page;
	}
	
	/**
	 * To set styles for the page.
	 * @param {Object} styleObj
	 */
	var setStyle = function (styleObj) {
		Utility.addStyles(this.element, styleObj);
	}
	
	/**
	 * To set the page margin
	 * @param {Object} side - "Left", "Right", "Top" or "Bottom" to denote whether it is left margin or right margin
	 * @param {Object} value - value in pixels
	 */
	var setPageMargin = function (side, value) {
		var dimension = (side == "Top" || side == "Bottom") ? "height" : "width";
		var pageSize = this.element.style[dimension] || "0px";
		var diff = Utility.removePx(pageSize) - Utility.removePx(value);
		if(diff > 0) {
			this.element.style["padding" + side] = value;
			this.element.style[dimension] = diff + "px";
		}
	}
	
	/**
	 * To set a string in the page number element
	 * @param {Object} pageNumberStr
	 */
	var setPageNumber = function (pageNumberStr, pageNumber) {
		this.pageNumberElmt.innerHTML = pageNumberStr;
		this.pageNumber = pageNumber;
	}
	
	/**
	 * To add a component to a page. It can be Label, Grid etc
	 * @param {Object} component
	 */
	var addComponent = function(component){
		component.page = this;
		component.canvas = this.canvas;		
		ComponentFunctions.addComponent.call(this, component);
	}
	
	/**
	 * Adding multiple components to the page
	 * @param {Object} components
	 */
	var addComponents = ComponentFunctions.addComponents;
	
	/**
	 * To get the remaining components in a page from a specified pixel position
	 * @param {Number} position
	 */
	var getComponentsFromPosition = ComponentFunctions.getComponentsFromPosition;
	
	/**
	 * To set the position of the page in the canvas as centralized
	 */
	function setPosition () {
		var borderLeft = this.element.style.borderLeft || "0px";
		borderLeft = borderLeft.split(" ")[0];
		var borderRight = this.element.style.borderRight || "0px";
		borderRight = borderRight.split(" ")[0];
		var paddingLeft = this.element.style.paddingLeft || "0px";
		var paddingRight = this.element.style.paddingRight || "0px";
		var width = this.element.style.width || "0px";
		var pageSpace = Utility.removePx(borderLeft) + Utility.removePx(paddingLeft) + Utility.removePx(width) + 
		Utility.removePx(paddingRight) + Utility.removePx(borderRight);
		var canvasWidth = Utility.removePx(this.canvas.element.style.width);
		if(this.canvas.isScrollVisible().vertical) {
			canvasWidth -= constants.SCROLL_WIDTH;
		}
		var margin = canvasWidth - pageSpace;
		if(margin > 0) {
			margin /= 2;
			this.element.style.marginLeft = margin + "px";
		}
	}
	
	/**
	 * To clone a page
	 */
	var clonePage = function () {
		var page = new Page(Utility.copyObject(this.inputObj));
		return page;
	}

	
	/**
	 * To get the next free position in the page. This can be called after adding variable height components like 
	 * Grids to know where the next element can be placed. The value returned from here can be used as the top for 
	 * the next element
	 */
	var nextPosition = function () {
		return Math.max.apply(Math, this.componentLimits);
	}
		
	return {
		_setComponent : _setComponent,
		setPageMargin : setPageMargin,
		setPosition : setPosition,
		setStyle : setStyle,
		addComponent : addComponent,
		addComponents : addComponents,
		nextPosition : nextPosition,
		clonePage : clonePage,
		constructor : Page,
		setPageNumber : setPageNumber,
		getComponentsFromPosition : getComponentsFromPosition
	}
})();

/**
 * Label class - to create labels
 */
var Label = function(inputObj){
	this.element = this._setComponent();
	this.inputObj = inputObj;
	this.element.innerHTML = inputObj.value;
	inputObj.styles && Utility.addStyles(this.element, inputObj.styles);
}

Label.prototype = (function () {
	var _setComponent = function () {
		var label = Utility.createHTMLElement("div");
		return label;
	}
	
	var setValue = function (value) {
		this.element.innerHTML = value;
		this.inputObj.value = value;
	}
	
	var cloneLabel = function () {
		var clone = new Label(Utility.copyObject(this.inputObj));
		return clone;
	}

	return {
		_setComponent : _setComponent,
		setValue : setValue,
		cloneLabel : cloneLabel,
		constructor : Label
	}
})();

/**
 * Groupbox class
 * @param {Object} inputObj
 */
var GroupBox = function(inputObj){
	this.element = this._setComponent();
	this.componentLimits = [];
	this.components = [];
	this.id = inputObj.id;
	inputObj.styles && Utility.addStyles(this.element, inputObj.styles);
}

GroupBox.prototype = (function () {
	/**
	 * Sets the HTML for the group box
	 */
	var _setComponent = function () {
		var box = Utility.createHTMLElement("div", {
			"class" : "groupbox"
		});
		return box;
	}

	/**
	 * To add a component to a page. It can be Label, Grid etc
	 * @param {Object} component
	 */
	var addComponent = function(component){
		component.groupBox = this;
		component.page = this.page;
		component.canvas = this.canvas;		
		ComponentFunctions.addComponent.call(this, component);
	}
	
	/**
	 * Adding multiple components to the page
	 * @param {Object} components
	 */
	var addComponents = ComponentFunctions.addComponents;

	/**
	 * To get the remaining components in a page from a specified pixel position
	 * @param {Number} position
	 */
	var getComponentsFromPosition = ComponentFunctions.getComponentsFromPosition;
	
	/**
	 * To set the id of the group box
	 * @param {String} id
	 */
	var setId = ComponentFunctions.setId;
	
	/**
	 * To set styles for the group box
	 * @param {Object} styles
	 */
	var setStyles = function (styles) {
		Utility.addStyles(this.element, styles);
	}	
				
	return {
		_setComponent : _setComponent,
		addComponent : addComponent,
		addComponents : addComponents,
		constructor : GroupBox,
		getComponentsFromPosition : getComponentsFromPosition,
		setId : setId,
		setStyles : setStyles
	}
})();

/**
 * Field class
 */
var Field = function (inputObj) {
	this.inputObj = inputObj;
	this.label = new Label(inputObj.label);
	this.field = new Label(inputObj.field);
	this.setFieldValue(inputObj.field.value);
	this.labelPosition = "";
	this.element = this._setComponent();
}

Field.prototype = (function(){
	/**
	 * Sets the field HTML
	 */
	var _setComponent = function () {
		this.label.element.className = "fieldlabel";
		this.field.element.className = "field";
		var element = Utility.createHTMLElement("div", {
			"class" : "fieldbox"
		});
		element.appendChild(this.label.element);
		element.appendChild(this.field.element);
		return element;
	}
	
	/**
	 * To set the value of the field
	 */
	var setFieldValue = function (value) {
		this.field.element.innerHTML = this.getDisplayValue(this, value);
		this.inputObj.field.value = value;
	}
	
	/**
	 * To set the label value of the field
	 */
	var setLabelValue = function (value) {
		this.label.element.innerHTML = value;
		this.inputObj.label.value = value;
	}
	
	/**
	 * To set the type of the field
	 * @param {Object} type- String, Number
	 */
	var setFieldType = function (type) {
		this.inputObj.field.type = type;
		this.setFieldValue(this.inputObj.field.value);
	}
	
	/**
	 * To get the display value for the field. Nothing is done if the cell type is String. For Number type cells
	 * the number is returned after replacing the . in the decimal with the decimalSeparator parameter provided by 
	 * the user in the properties object of the canvas
	 * @param {Object} fieldObj - field object
	 * @param {Object} value
	 */
	var getDisplayValue = function (fieldObj, value) {
		if(fieldObj.canvas) {
			return Utility.getValueByType(fieldObj.field.inputObj.type, value, fieldObj.canvas.properties);			
		}
		return value;
	} 
		
	/**
	 * To set the styles of the field
	 * @param {Object} styleObj - can have 2 properties
	 *  - label - object with label styles
	 *  - field - object with  field styles
	 */
	var setStyles = function(styleObj) {
		styleObj.label && Utility.addStyles(this.label.element, styleObj.label, this.inputObj.label.styles);
		styleObj.field && Utility.addStyles(this.field.element, styleObj.field, this.inputObj.field.styles);
	}
	
	/**
	 * To clone the field
	 */
	var cloneField = function () {
		var clone = new Field(Utility.copyObject(this.inputObj));
		return clone;
	}
	
	/**
	 * To set the label position
	 * @param {String} position - Top - label on top and field below, Left - default. Label left aligned
	 * Right - Label right aligned
	 */
	var setLabelPosition = function (position) {
		if(this.labelPosition == position) {
			return;
		} else if(position == "Left") {
			this.setStyles({
				label : {
					"float" : "left",
					textAlign : "left"
				},
				field : {
					"float" : "left"
				}				
			});
		} else if(position == "Right") {
			this.setStyles({
				label : {
					"float" : "left",
					textAlign : "right"
				},
				field : {
					"float" : "left"
				}				
			});			
		} else if (position == "Top") {
			this.setStyles({
				label : {
					clear : "both"
				},
				field : {
					clear : "both",
					textAlign : "left"
				}
			})			
		}
		this.labelPosition = position;
	}
	
	return {
		_setComponent : _setComponent,
		setLabelValue : setLabelValue,
		setFieldValue : setFieldValue,
		cloneField : cloneField,
		setStyles : setStyles,
		setLabelPosition : setLabelPosition,
		setFieldType : setFieldType,
		getDisplayValue : getDisplayValue,
		constructor : Field
	}
})();

/**
 * Grid class
 */
var Grid = function (inputObj) {
	this.inputObj = inputObj;
	this.cellTypes = [];
	this.element = this._setComponent(inputObj.styles);
	this.continuation = {
		pages : [],
		grids : []
	}
	this.data;	//Data displayed in the grid
	this.id = inputObj.id;
}

Grid.prototype = (function () {
	/**
	 * Sets the component HTML
	 * @param {Object} styles
	 */
	var _setComponent = function (styles) {
		var container = new GroupBox({
			styles : {
				top : styles.top
			}
		});
		this.table = Utility.createHTMLElement("table", {
			"class" : "grid",
			cellspacing : 0
		}, container.element);
		this.head = Utility.createHTMLElement("thead", undefined, this.table);
		this.body = Utility.createHTMLElement("tbody", undefined, this.table);
		for(var i = 0; i < styles.cellWidth.length; i++) {
			var headerCell = Utility.createHTMLElement("th", undefined, this.head);
			headerCell.style.height = styles.headerHeight;
			headerCell.innerHTML = this.inputObj.headers[i].title;
			this.cellTypes.push(this.inputObj.headers[i].type);
			headerCell.style.width = styles.cellWidth[i];
		}
		return container.element;
	}
	
	/**
	 * Sets the data in the grid. If the page gets over, rows are added till the end and then the remaining rows are returned as an array.
	 * If all the rows were inserted, then an empty array is returned
	 * @param {Object} dataObj
	 * @param {Object} remainingComponents - components passed here will be added to the end of the grid after setting the data
	 */
	var setData = function (dataObj, remainingComponents) {
		this.data && clearData(this);
		if(!remainingComponents) {
			remainingComponents = getRemainingComponents(this, this.lastPage || this.page, this.lastGrid || this);
		}
		this.data = dataObj;
		var remaining = addRows(this, dataObj);
		this.page.addComponent(this);		//For recalculating the tops and heights in the componentPosition
		while(remaining.length > 0) {
			var page = this.page.clonePage();
			this.continuation.pages.push(page);
			this.canvas.addPage(page);
			var grid = this.cloneGrid();
			grid.setId = this.id;
			this.continuation.grids.push(grid);
			page.addComponent(grid);				
			remaining = addRows(grid, remaining);
			page.addComponent(grid);
		}
		remainingComponents.length > 0 && addRemainingComponents(this, remainingComponents);
		return page || this.page;
	}
	
	/**
	 * To clear the data in a grid by removing the intermediate pages
	 * @param {Object} that
	 * @return remainingComponents
	 */
	var clearData = function (that) {
		that.body.innerHTML = "";
		var gridPages = that.continuation.pages;
		var grids = that.continuation.grids;
		for (var i = that.continuation.pages.length - 2; i >= 0; i--){
			that.canvas.removePage(gridPages[i].pageNumber);
		}
		that.lastPage = gridPages[gridPages.length - 1];
		that.lastGrid = grids[grids.length - 1];
		that.continuation.pages = that.continuation.grids = that.data = [];		
	}
	
	/**
	 * To get the components added in the report after the specified page and grid
	 * @param {Object} that
	 * @param {Object} lastPage
	 * @param {Object} lastGrid
	 */
	var getRemainingComponents = function (that, lastPage, lastGrid) {
		var remainingComponents = lastPage.getComponentsFromPosition(Utility.getOffsetHeight(lastGrid.element));
		var otherPageComps = that.canvas.getComponentsFromPage(lastPage.pageNumber + 1);
		otherPageComps.length > 0 && remainingComponents.push(otherPageComps);
		remainingComponents.length > 0 && that.canvas.removePage(lastPage.pageNumber);
		return remainingComponents;
	}
	
	/**
	 * To add the remaining components to the end of the grid
	 * @param {Object} that
	 * @param {Object} remainingComponents
	 */
	var addRemainingComponents = function (that, remainingComponents) {
		remainingComponents = removeDuplicates(remainingComponents);
		var lastPage = that.continuation.pages[that.continuation.pages.length - 1];
		for(var i = 0; i < remainingComponents.length; i++) {
			var currentComponent = remainingComponents[i];
			if(currentComponent.constructor == Grid) {
				remainingComponents.splice(i+1);
				currentComponent.setData(currentComponent.data, remainingComponents);
				return;
			} else {
				lastPage.addComponent(currentComponent);
			}
		}				
	}
	
	/**
	 * Checks the id property of each component and then removes components with the same id
	 * @param {Object} components
	 */
	var removeDuplicates = function (components) {
		var idObj = {};
		var returnArr = [];
		do {
			var comp = components.pop();
			if(!comp.id || !idObj[comp.id]) {
				returnArr.unshift();
				idObj[comp.id] = true;
			}			
		} while(components.length > 0);
		return returnArr;
	}

	/**
	 * To set the headers in the grid
	 * @param : headers array
	 */
	var setHeaders = function (headers) {
		var headerCells = this.head.childNodes;
		for(var i = 0; i < headerCells.length; i++) {
			headerCells[i].innerHTML = headers[i];
		}
	}
		
	/**
	 * To add rows to a grid
	 * @param {Object} grid
	 * @param {Object} data
	 */
	var addRows = function(grid, data){
		var actualTableTop = Utility.getComputedStyle(grid.element, "top");
		var pageHeight = Utility.getComputedStyle(grid.page.element, "height");
		for (i = 0; i < data.length; i++) {
			var row = Utility.createHTMLElement("tr", undefined, grid.body);
			for (var j = 0; j < data[i].length; j++) {
				var cell = Utility.createHTMLElement("td", undefined, row);
				cell.actualValue = data[i][j];
				cell.innerHTML = getDisplayValue(grid, j, data[i][j]);
				cell.style.textAlign = grid.inputObj.styles.cellAlign[j];
			}
			var actualTableHeight = Utility.getComputedStyle(grid.element, "height");
			var remainingSpace = (pageHeight - (actualTableHeight + actualTableTop));
			if (remainingSpace < 0) {
				grid.body.removeChild(row);
				return data.slice(i, data.length);
			}
		}
		return [];
	}
	
	/**
	 * To get the display value for the cell. Nothing is done if the cell type is String. For Number type cells
	 * the number is returned after replacing the . in the decimal with the decimalSeparator parameter provided by 
	 * the user in the inputObj of the grid
	 * @param {Object} grid - grid object
	 * @param {Number} columnNumber - number of the column starting from 0
	 * @param {Object} value
	 */
	var getDisplayValue = function (grid, columnNumber, value) {
		var inputObj = grid.inputObj;
		return Utility.getValueByType(inputObj.headers[columnNumber].type, value, grid.canvas.properties);
	} 
	
	/**
	 * To set the id of a Grid
	 */
	var setId = ComponentFunctions.setId;
	
	/**
	 * To get a clone for the grid
	 */
	var cloneGrid = function () {
		var grid = new Grid(Utility.copyObject(this.inputObj));
		grid.element.style.top = Utility.getComputedStyle(this.page.element, "padding-top") + "px";
		grid.id = "";
		return grid;
	}
	
	return {
		_setComponent : _setComponent,
		setData : setData,
		cloneGrid : cloneGrid,
		constructor : Grid,
		setId : setId,
		setHeaders : setHeaders
	}
})();

/**
 * Utility functions
 */
var Utility = (function () {
	/**
	 * To create an HTML element
	 * type - type of the element to be created
	 * attributes - object containing the list of attributes
	 * parent - parent element id or the parent element itself to which the created element is to be attached
	 */	
	var createHTMLElement = function (type, attributes, parent) {
		var element = document.createElement(type);
		if(attributes) {
			for(var i in attributes) {
				element.setAttribute(i, attributes[i]);
			}
		}
		if(parent) {
			parent = parent.constructor == String ? document.getElementById(parent) : parent;
			parent.appendChild(element);
		}
		return element;
	}	
	
	/**
	 * Appends "px" to the end of a string if it is not already added.
	 * @param {Object} str
	 */
	var addPx = function (str) {
		return (/px$/.test(str)) ? str : (str + "px");			
	}	
	
	/**
	 * To remove the px from the end of a string and return it as a number
	 * @param {Object} str
	 */
	var removePx = function (str) {
		return Number(str.replace("px","")) || 0;	//Sometimes auto may create problem. This is defaulted to 0
	}
	
	/**
	 * Add styles to an element
	 * @param {Object} element
	 * @param {Object} styles - styles object
	 * @param {Object} storeObj - the styles applied to the element are also added as parameters in this object and
	 * returned. This is useful for editing the inputObj when the user sets some styles
	 */
	var addStyles = function (element, styles, storeObj) {
		for(var i in styles) {
			element.style[i] = styles[i];
			if(storeObj) {
				storeObj[i] = styles[i];			
			}
		}		
	}
	
	/**
	 * To get the computed style of the element
	 * @param {Object} element
	 * @param {Object} property
	 */
	var getComputedStyle = function (element, property) {
		return this.removePx(window.getComputedStyle(element, null).getPropertyValue(property));
	}
	
	/**
	 * To convert a value into a suitable display value based on the properties of the canvas
	 * @param {Object} type - Number, String
	 * @param {Object} value - value to be transformed
	 * @param {Object} properties - properties object of the canvas
	 */
	var getValueByType = function (type, value, properties) {
		if(type == "Number") {
			return value.toString().replace(".", properties.decimalSeparator);
		}
		return value;
	}
	
	/**
	 * To create a copy of an object
	 * @param {Object} srcObject
	 * @param {Object} targetObject
	 */
	var copyObject = function(srcObject, targetObject) {
		targetObject = targetObject || (srcObject instanceof Array ? [] : {});
		for(var i in srcObject) {
			if (typeof(srcObject[i]) == "object") {
				targetObject[i] = copyObject(srcObject[i]);
			} else {
				targetObject[i] = srcObject[i];
			}
		}
		return targetObject;
	}
	
	/**
	 * To get the top + height of an element. The pixel position where the element ends w.r.t a page
	 * @param {Object} element
	 */
	var getOffsetHeight = function (element) {
		var computedTop = Utility.getComputedStyle(element, "top");
		var computedHeight = Utility.getComputedStyle(element, "height");
		return computedHeight + computedTop;		
	}
	
	return {
		createHTMLElement : createHTMLElement,
		addPx : addPx,
		addStyles : addStyles,
		removePx : removePx,
		getComputedStyle : getComputedStyle,
		getValueByType : getValueByType,
		copyObject : copyObject,
		getOffsetHeight : getOffsetHeight
	}
})();